<template>
  <div class="tree-select-single" v-click-outside="closeDropdown">
    <div
      class="dropdown-trigger"
      :class="{ 'is-active': isOpen, 'is-disabled': disabled }"
      @click="toggleDropdown"
    >
      <div class="selected-text">
        <span v-if="selectedLabel" class="selected-info">
          {{ selectedLabel }}
        </span>
        <span v-else class="placeholder">{{ placeholder }}</span>
      </div>
      <div class="dropdown-icon">
        <div class="arrow-down" :class="{ 'is-rotate': isOpen }"></div>
      </div>
    </div>

    <div class="dropdown-menu" v-show="isOpen">
      <!-- 操作栏 -->
      <div class="tree-toolbar">
        <div class="toolbar-left">
          <span
            class="toolbar-btn"
            @click="expandAll"
            :class="{ 'is-active': allExpanded }"
          >
            全部展开
          </span>
          <span
            class="toolbar-btn"
            @click="collapseAll"
            :class="{ 'is-active': !allExpanded }"
          >
            全部收起
          </span>
        </div>
        <div class="toolbar-right">
          <span
            class="toolbar-btn clear-all"
            @click="clearSelection"
            v-if="selectedValue"
          >
            清空
          </span>
        </div>
      </div>

      <!-- 树形结构 -->
      <div class="tree-container">
        <TreeNodeSingle
          v-for="node in options"
          :key="getNodeValue(node)"
          :node="node"
          :level="0"
          :selected-value="selectedValue"
          :expanded-keys="expandedKeys"
          :field-names="fieldNames"
          @toggle-expand="toggleExpand"
          @select-node="selectNode"
        />
      </div>
    </div>
  </div>
</template>

<script>
import TreeNodeSingle from "./TreeNodeSingle.vue";

export default {
  name: "TreeSelectSingle",
  components: {
    TreeNodeSingle,
  },
  props: {
    // 支持v-model绑定的值（单个值）
    modelValue: {
      type: [String, Number],
      default: "",
    },
    // 树形选项数据
    options: {
      type: Array,
      default: () => [],
    },
    // 占位文本
    placeholder: {
      type: String,
      default: "请选择",
    },
    // 是否禁用
    disabled: {
      type: Boolean,
      default: false,
    },
    // 默认展开所有节点
    defaultExpandAll: {
      type: Boolean,
      default: false,
    },
    // 自定义字段名配置
    fieldNames: {
      type: Object,
      default: () => ({
        label: "label",
        value: "value",
        children: "children",
      }),
    },
  },
  emits: ["update:modelValue", "change"],
  data () {
    return {
      isOpen: false,
      selectedValue: "",
      expandedKeys: new Set(),
    };
  },
  computed: {
    // 选中项的显示文本
    selectedLabel () {
      if (!this.selectedValue) return "";

      const selectedNode = this.findNodeByValue(this.selectedValue);
      return selectedNode ? this.getNodeLabel(selectedNode) : "";
    },

    // 是否全部展开
    allExpanded () {
      const allNodes = this.getAllNodes();
      const hasChildrenNodes = allNodes.filter(
        (node) =>
          this.getNodeChildren(node) && this.getNodeChildren(node).length > 0,
      );
      return hasChildrenNodes.every((node) =>
        this.expandedKeys.has(this.getNodeValue(node)),
      );
    },
  },
  watch: {
    modelValue: {
      immediate: true,
      handler (newVal) {
        this.selectedValue = newVal || "";
      },
    },
  },
  mounted () {
    if (this.defaultExpandAll) {
      this.expandAll();
    }
  },
  directives: {
    // 点击外部关闭下拉菜单
    "click-outside": {
      mounted (el, binding) {
        el._clickOutside = (event) => {
          if (!(el === event.target || el.contains(event.target))) {
            binding.value(event);
          }
        };
        document.addEventListener("click", el._clickOutside);
      },
      unmounted (el) {
        document.removeEventListener("click", el._clickOutside);
      },
    },
  },
  methods: {
    // 切换下拉菜单
    toggleDropdown () {
      if (!this.disabled) {
        this.isOpen = !this.isOpen;
      }
    },

    // 关闭下拉菜单
    closeDropdown () {
      this.isOpen = false;
    },

    // 切换节点展开状态
    toggleExpand (nodeValue) {
      if (this.expandedKeys.has(nodeValue)) {
        this.expandedKeys.delete(nodeValue);
      } else {
        this.expandedKeys.add(nodeValue);
      }
    },

    // 选择节点（只能选择叶子节点）
    selectNode (node) {
      const nodeChildren = this.getNodeChildren(node);

      // 只允许选择叶子节点
      if (nodeChildren && nodeChildren.length > 0) {
        return;
      }

      const nodeValue = this.getNodeValue(node);
      this.selectedValue = nodeValue;
      this.emitChange();

      // 选择后自动关闭下拉
      this.closeDropdown();
    },

    // 展开所有节点
    expandAll () {
      const allNodes = this.getAllNodes();
      allNodes.forEach((node) => {
        const nodeChildren = this.getNodeChildren(node);
        if (nodeChildren && nodeChildren.length > 0) {
          this.expandedKeys.add(this.getNodeValue(node));
        }
      });
    },

    // 收起所有节点
    collapseAll () {
      this.expandedKeys.clear();
    },

    // 清空选择
    clearSelection () {
      this.selectedValue = "";
      this.emitChange();
    },

    // 获取所有节点（包括子节点）
    getAllNodes () {
      const nodes = [];
      const traverse = (nodeList) => {
        nodeList.forEach((node) => {
          nodes.push(node);
          const nodeChildren = this.getNodeChildren(node);
          if (nodeChildren && nodeChildren.length > 0) {
            traverse(nodeChildren);
          }
        });
      };
      traverse(this.options);
      return nodes;
    },

    // 根据值查找节点
    findNodeByValue (value, nodeList = this.options) {
      for (const node of nodeList) {
        if (this.getNodeValue(node) === value) {
          return node;
        }
        const nodeChildren = this.getNodeChildren(node);
        if (nodeChildren && nodeChildren.length > 0) {
          const found = this.findNodeByValue(value, nodeChildren);
          if (found) {
            return found;
          }
        }
      }
      return null;
    },

    // 触发change事件
    emitChange () {
      this.$emit("update:modelValue", this.selectedValue);
      this.$emit("change", this.selectedValue);
    },

    // 获取节点的指定字段值
    getNodeValue (node) {
      return node[this.fieldNames.value];
    },

    getNodeLabel (node) {
      return node[this.fieldNames.label];
    },

    getNodeChildren (node) {
      return node[this.fieldNames.children];
    },
  },
};
</script>

<style lang="less" scoped>
@primary-color: #4b58dc;
@transition-time: 0.3s;

.tree-select-single {
  position: relative;
  flex: 1;
  user-select: none;
  text-align: left;

  .dropdown-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 40px;
    padding: 0 15px;
    background-color: #fff;
    border: 1px solid #dcdfe6;
    border-radius: 4px;
    cursor: pointer;
    transition: all @transition-time ease;
    position: relative;
    overflow: hidden;

    &:after {
      content: "";
      position: absolute;
      top: 0;
      left: -75%;
      width: 50%;
      height: 100%;
      background: linear-gradient(
        to right,
        rgba(255, 255, 255, 0),
        rgba(255, 255, 255, 0.4),
        rgba(255, 255, 255, 0)
      );
      transform: skewX(-15deg);
      transition: all 0.5s ease;
      opacity: 0;
    }

    &:hover {
      border-color: @primary-color;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);

      &:after {
        left: 120%;
        opacity: 1;
      }
    }

    &.is-active {
      border-color: @primary-color;
      box-shadow: 0 0 0 2px rgba(75, 88, 220, 0.1);
      background: linear-gradient(
        to right,
        rgba(75, 88, 220, 0.02),
        rgba(255, 255, 255, 0.8),
        rgba(75, 88, 220, 0.02)
      );
    }

    &.is-disabled {
      background-color: #f5f7fa;
      border-color: #e4e7ed;
      color: #c0c4cc;
      cursor: not-allowed;
    }

    .selected-text {
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      font-size: 14px;
      color: #606266;

      .selected-info {
        color: @primary-color;
        font-weight: 500;
      }

      .placeholder {
        color: #c0c4cc;
      }
    }

    .dropdown-icon {
      margin-left: 8px;
      color: #c0c4cc;
      transition: transform @transition-time ease;

      .arrow-down {
        width: 0;
        height: 0;
        border-left: 4px solid transparent;
        border-right: 4px solid transparent;
        border-top: 6px solid #c0c4cc;
        transition: transform @transition-time ease;

        &.is-rotate {
          transform: rotate(180deg);
        }
      }
    }
  }

  .dropdown-menu {
    position: absolute;
    top: calc(100% + 5px);
    left: 0;
    width: 100%;
    max-height: 300px;
    background-color: #fff;
    border-radius: 4px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
    z-index: 10;
    animation: dropdown-in @transition-time ease;
    overflow: hidden;

    .tree-toolbar {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 8px 15px;
      background: linear-gradient(
        to right,
        rgba(75, 88, 220, 0.05),
        rgba(255, 255, 255, 0.8),
        rgba(75, 88, 220, 0.05)
      );
      border-bottom: 1px solid #f0f0f0;
      font-size: 12px;

      .toolbar-left,
      .toolbar-right {
        display: flex;
        gap: 10px;
      }

      .toolbar-btn {
        padding: 4px 8px;
        border-radius: 3px;
        cursor: pointer;
        transition: all @transition-time ease;
        color: #909399;

        &:hover {
          color: @primary-color;
          background-color: rgba(75, 88, 220, 0.1);
        }

        &.is-active {
          color: @primary-color;
          background-color: rgba(75, 88, 220, 0.1);
        }

        &.clear-all {
          color: #f56c6c;
        }
      }
    }

    .tree-container {
      max-height: 250px;
      overflow-y: auto;

      &::-webkit-scrollbar {
        width: 6px;
      }

      &::-webkit-scrollbar-thumb {
        background-color: #dcdfe6;
        border-radius: 3px;
      }

      &::-webkit-scrollbar-track {
        background-color: #f5f7fa;
      }
    }
  }
}

@keyframes dropdown-in {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
</style>
